/**
 * @file misc.c
 * @brief
 *
 * @author yemt@witsi.cn
 * @date 2012-3-10
 * @version T1.MISC.02
 */
#include "config.h"
#include "init.h"
#include "def.h"
#include "arch.h"
#include "dev.h"
#include "syscfg.h"
#include "init.h"
#include "kernel.h"
#include <string.h>
#include "autoconfig.h"
#include "config.h"
#include "clk.h"


#ifndef CONFIG_DEF_CLK
#error CONFIG_DEF_CLK undefined
#endif

#define NV_BASE         0xBA000000
#define NV_SIZE         4096

#define NV_KERNEL_USE_LEN		(0X40)
#define KERNEL_NVSRAM_TEST	"\xA5\x5A\xAA\x55"      

void reset(void)
{
        SYSCFG->SYS_OSC |= 0x55AA;
}

uint32_t get_sys_timer(void)
{
	return read_c0_count();
}

typedef struct {
	uint32_t NVSRAM_CTL;
	uint32_t DEF_CTL;
	uint32_t ATP_SEL;
	uint32_t TEMP_CFG;
	uint32_t ROSC_PD;
	uint32_t T1_TIME;
	uint32_t T2_TIME;
	uint32_t IN_MESH_T345;
	uint32_t EX_MESH_T345;
	uint32_t SW_T345;
	uint32_t TEMP_TIME;
	uint32_t RVDT_TIME;
	uint32_t DEBOUNCE_N;
	uint32_t LOCK_TIME;
}sec_mode_t;


static const sec_mode_t def_secmod = {
	.NVSRAM_CTL = NVSRAM_RW_EN,			
	.DEF_CTL = CONFIG_DEF_CTL,
	.ATP_SEL = CONFIG_DEF_ATP,
	.TEMP_CFG = CONFIG_DEF_TEMP,	
	.T1_TIME = DEF_T1_TIME_4MS,
	.T2_TIME = DEF_T2_TIME_62_5US,
	.IN_MESH_T345 = DEF_T5_250US | DEF_T4_500US | DEF_T3_1MS,
	.EX_MESH_T345 = DEF_T5_250US | DEF_T4_500US | DEF_T3_1MS,
	.SW_T345 = DEF_T5_250US | DEF_T4_500US | DEF_T3_1MS,
	.TEMP_TIME = DEF_TEMP_TIME_375US,
	.RVDT_TIME = DEF_RVDT_TIME_WIDTH_187_5US | DEF_RVDT_TIME_INTV_64MS,
	.DEBOUNCE_N = 5,
	.LOCK_TIME = DEF_LOCK_TIME_250MS,
};

void security_enable(void *mode)
{
	sec_mode_t * pm = NULL;
	
	if (mode) pm = (sec_mode_t *)mode;
	if (pm == NULL) pm = (sec_mode_t *)&def_secmod;
	DEF->T1_TIME = pm->T1_TIME;
	DEF->T2_TIME = pm->T2_TIME;
	DEF->IN_MESH_T345 = pm->IN_MESH_T345;                                                                        
	DEF->EX_MESH_T345 = pm->EX_MESH_T345;
	DEF->SW_T345 = pm->SW_T345;
	DEF->TEMP_TIME = pm->TEMP_TIME;
	DEF->RVDT_TIME = pm->RVDT_TIME;
	DEF->LOCK_TIME = pm->LOCK_TIME;
	DEF->DEBOUNCE_N = pm->DEBOUNCE_N;

	DEF->LOCK_EN &= ~PARA_LOCK;

	DEF->ATP_SEL = pm->ATP_SEL;
	DEF->TEMP_CFG = pm->TEMP_CFG;
	DEF->LOCK_EN &= ~ANA_LOCK;

	DEF->DEF_CTL = pm->DEF_CTL;

	DEF->LOCK_EN &= ~DEF_LOCK;

	DEF->NVSRAM_CTL = pm->NVSRAM_CTL;
	DEF->LOCK_EN &= ~(NVSRAM_LOCK);

	while ((DEF->LOCK_EN & LOCK_MASK) != LOCK_MASK);
	
	rtc_enable();
}

uint32_t security_status(void)
{
        return DEF->INT_STA;
}

void security_status_clear(void)
{
        DEF->ERR33_CLR = 0;
        while (DEF->ERR33_CLR == 0);
        DEF->INT_CLR = 0xFFFFFFFF;
        mdelay(500);
        DEF->ERR33_CLR = 0;
        while (DEF->ERR33_CLR == 0);
        DEF->INT_CLR = 0xFFFFFFFF;
}


static int nv_write(uint32_t offset, uint8_t * buf, int buflen)
{
	uint32_t data, addr, diffb = 0, len = 0;
	uint8_t tmp[4];

	addr = NV_BASE + offset;
	diffb = addr & 0x3;
	if (diffb) {
		addr &= ~0x3;
		memcpy(tmp, (volatile uint8_t *)addr, 4);
		len = 4 - diffb;
		if (buflen < len) len = buflen;
		memcpy(tmp + diffb, buf, len);
        memcpy(&data, tmp, 4);
        *(volatile uint32_t *)addr = data;
		buflen -= len;
		buf += len;
		addr += 4;
	}
	if (buflen > 3) {
		len = buflen & ~0x3;
        buflen -= len;
        while (len) {
            memcpy(&data, buf, 4);
            *(volatile uint32_t *)addr = data;
            addr += 4;
            buf += 4;
            len -= 4;
            
        }
	}
	if (buflen) {
		memcpy(tmp, (volatile uint8_t *)addr, 4);
		memcpy(tmp, buf, buflen);
        memcpy(&data, tmp, 4);
        *(volatile uint32_t *)addr = data;
	}
	return 0;
}

#if !defined(CONFIG_NVSRAM_FLASH)

int _nv_write(uint32_t offset, uint8_t * buf, int buflen)
{
	down_pclk1();
	nv_write(offset + NV_KERNEL_USE_LEN, buf, buflen);
	up_pclk1();
	return buflen;
}

int _nv_read(uint32_t offset, uint8_t * buf, int buflen)
{
	down_pclk1();
	memcpy(buf, (uint8_t *)(offset + NV_BASE + NV_KERNEL_USE_LEN), buflen);
	up_pclk1();

	return buflen;
}
void _nv_clear(uint32_t offset,  uint32_t size)
{
	down_pclk1();
	memset((uint8_t *)(offset + NV_BASE + NV_KERNEL_USE_LEN), 0x00, size);
	up_pclk1();

}

#endif

#if 0 ////////////////debug
void enable_nvsram_32k(void)
{
	down_pclk1();
	DEF->NVSRAM_CTL = NVSRAM_RW_EN | NVSRAM_CLK_SEL_32K;
	DEF->LOCK_EN &= ~(NVSRAM_LOCK);
	while ((DEF->LOCK_EN & LOCK_MASK) != LOCK_MASK);
        up_pclk1();
}
static void disable_nvsram_32k(void)
{
	DEF->NVSRAM_CTL = NVSRAM_RW_EN;
	DEF->LOCK_EN &= ~(NVSRAM_LOCK);
	while ((DEF->LOCK_EN & LOCK_MASK) != LOCK_MASK);
}
#endif

void security_disable(void)
{
	DEF->DEF_CTL = DEF_EN;
	DEF->LOCK_EN &= ~(DEF_LOCK);
	while ((DEF->LOCK_EN & LOCK_MASK) != LOCK_MASK);
}

static int nv_write_read_test(void)
{
	uint8_t buf[4];
	
	nv_write(0x20, (uint8_t *)KERNEL_NVSRAM_TEST, 4);
	memcpy(buf, (uint8_t *)(NV_BASE + 0X20), 4);

	return memcmp(buf, KERNEL_NVSRAM_TEST, 4);
}

static int gpower_flag = 0;
uint8_t get_power_status(void)
{
	return gpower_flag ? 1 : 0;
}

static int misc_arch_init(void)
{
	down_pclk1();
	DEF->DEF_CTL = CONFIG_DEF_CTL;
#ifdef CONFIG_POWERDOWN_32KMODE
        disable_nvsram_32k();

#else
	gpower_flag = nv_write_read_test();
	if (gpower_flag) {
		DEF->NVSRAM_CTL = NVSRAM_RW_EN;
		DEF->LOCK_EN &= ~(NVSRAM_LOCK | DEF_LOCK);
		while ((DEF->LOCK_EN & LOCK_MASK) != LOCK_MASK); 
		rtc_enable();
	}

#endif

	up_pclk1();
	
	return 0;
}

arch_initcall(misc_arch_init);

#if defined(CONFIG_SECURITY_PCI)	

extern int  pci_selftest( void );
#define SECURITY_TIMER0       0
#define SECURITY_CLOCK       (CONFIG_MOSC / 256)		
#define SECURITY_CHECKTIME   ( 60 *60 * 12  )		//12hours mandatory	
#define SECURITY_DELAY       (SECURITY_CHECKTIME * SECURITY_CLOCK)	
static void timer_security_interrupt( void )
{	
	timer_hw_stop(SECURITY_TIMER0);
	/* PCI Security Check */
	if( pci_selftest( ) != 0 )
		reset( );
	/* Resource Check */
	if( resource_init( ) != 0 )
		reset( );
	timer_hw_start(SECURITY_TIMER0);

}
void security_check_init( void )
{
	timer_hw_init(SECURITY_TIMER0, TIMER_PERIODIC, SECURITY_CLOCK,
                        SECURITY_DELAY, timer_security_interrupt);
	timer_hw_start(SECURITY_TIMER0);
}

#else

void security_check_init( void )
{
	return;
}

#endif

